﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Nomo.CoreModule
{
    /// <summary>
    /// 提供一些用于补充 <see cref="System.Linq.Enumerable"/> 的扩展方法。
    /// </summary>
    public static class NomoEnumerable
    {
        #region 增、删、改、查

        /// <summary>
        /// 返回序列里最“合适”的成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="predicate">对于 <paramref name="predicate"/> 的参数 a 和 b ，如果要使得 a 比 b 更“合适”，请确保 <paramref name="predicate"/> 返回 <see langword="true"/> .</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>序列里最“合适”的成员。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="predicate"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="source"/> 中没有成员。</exception>
        public static TSource Most<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            var most       = default(TSource);
            var hasElement = false;
            foreach (var element in source)
            {
                if (!hasElement)
                {
                    hasElement = true;
                    most       = element;
                    continue;
                }
                if (predicate(element, most))
                {
                    most = element;
                }
            }

            return hasElement ? most : throw new InvalidOperationException($"{nameof(source)} 中没有成员。");
        }

        /// <summary>
        /// 返回序列里最“合适”的成员。如果序列中没有成员，则返回默认值。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="predicate">对于 <paramref name="predicate"/> 的参数 a 和 b ，如果要使得 a 比 b 更“合适”，请确保 <paramref name="predicate"/> 返回 <see langword="true"/> .</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>序列里最“合适”的成员，或（当序列中没有成员时）默认值。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="predicate"/> 为 <see langword="null"/> .</exception>
        public static TSource MostOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            var most       = default(TSource);
            var hasElement = false;
            foreach (var element in source)
            {
                if (!hasElement)
                {
                    hasElement = true;
                    most       = element;
                    continue;
                }
                if (predicate(element, most))
                {
                    most = element;
                }
            }

            return most;
        }

        /// <summary>
        /// 返回一个值，这个值指示 <paramref name="source"/> 中与 <paramref name="element"/> 匹配的第一个成员的索引。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="element">成员。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns><paramref name="source"/> 中与 <paramref name="element"/> 匹配的最后一个成员的索引。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        public static int IndexOf<TSource>(this IEnumerable<TSource> source, TSource element)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            switch (source)
            {
                case IList<TSource> list:
                    return list.IndexOf(element);
                case IList nonGenericList:
                    return nonGenericList.IndexOf(element);
                default:
                    return source.ToList().IndexOf(element);
            }
        }

        /// <summary>
        /// 获取二维数组中与 <paramref name="element"/> 匹配的第一个成员的索引。
        /// </summary>
        /// <param name="array">二维数组。</param>
        /// <param name="element">成员。</param>
        /// <param name="index0">第一维的索引。</param>
        /// <param name="index1">第二维的索引。</param>
        /// <typeparam name="T"><paramref name="array"/> 中的成员的类型。</typeparam>
        /// <returns>如果找到了匹配成员，则返回 <see langword="true"/> ，否则返回 <see langword="false"/> .</returns>
        /// <exception cref="ArgumentNullException"><paramref name="array"/> 为 <see langword="null"/> .</exception>
        public static bool IndexOf<T>(this T[,] array, T element, out int index0, out int index1)
        {
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }

            var index = -1;
            foreach (var arrayElement in array)
            {
                checked
                {
                    ++index;
                }

                if (Equals(arrayElement, element))
                {
                    var length1 = array.GetLength(1);
                    index0 = index / length1;
                    index1 = index % length1;
                    return true;
                }
            }

            index0 = -1;
            index1 = -1;
            return false;
        }

        /// <summary>
        /// 返回一个值，这个值指示 <paramref name="source"/> 中与 <paramref name="element"/> 匹配的最后一个成员的索引。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="element">成员。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns><paramref name="source"/> 中与 <paramref name="element"/> 匹配的第一个成员的索引。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        public static int LastIndexOf<TSource>(this IEnumerable<TSource> source, TSource element)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            switch (source)
            {
                case List<TSource> list:
                    return list.LastIndexOf(element);
                case TSource[] array:
                    return Array.LastIndexOf(array, element);
                default:
                    return source.ToList().LastIndexOf(element);
            }
        }

        /// <summary>
        /// 在序列的指定索引处插入一个成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="index">索引。</param>
        /// <param name="insertElement">要插入序列的成员。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>返回一个新的序列，它是序列在指定索引处插入一个成员后的结果。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> 小于 0 ，或大于或等于序列的长度。</exception>
        public static IEnumerable<TSource> Insert<TSource>(this IEnumerable<TSource> source, int index, TSource insertElement)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (index < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(index));
            }

            var i        = -1;
            var inserted = false;
            foreach (var element in source)
            {
                checked
                {
                    ++i;
                }
                if (!inserted && i == index)
                {
                    inserted = true;
                    yield return insertElement;
                }
                yield return element;
            }
            if (!inserted)
            {
                throw new ArgumentOutOfRangeException(nameof(index));
            }
        }

        /// <summary>
        /// 在序列的每两个成员之间插入指定的新成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="insertElement">要插入到序列每两个成员之间的新成员。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>返回一个新的序列，它是序列的每两个成员之间插入指定的新成员之后的结果。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> InsertEach<TSource>(this IEnumerable<TSource> source, TSource insertElement)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var hasElement = false;
            foreach (var element in source)
            {
                if (!hasElement)
                {
                    hasElement = true;
                    yield return element;
                    continue;
                }

                yield return insertElement;
                yield return element;
            }
        }

        /// <summary>
        /// 根据插入成员求值器函数，在序列的每两个成员之间插入新成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="insertElementEvaluator">根据插入位置前后两个成员来确定插入成员的求值器函数。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>返回一个新的序列，它是序列的每两个成员间插入根据指定的插入成员求值器函数得到的成员之后的结果。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="insertElementEvaluator"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> InsertEach<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> insertElementEvaluator)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (insertElementEvaluator == null)
            {
                throw new ArgumentNullException(nameof(insertElementEvaluator));
            }

            var first    = default(TSource);
            var hasFirst = false;
            foreach (var element in source)
            {
                if (!hasFirst)
                {
                    hasFirst = true;
                    first    = element;

                    yield return element;

                    continue;
                }

                yield return insertElementEvaluator(first, element);

                first = element;

                yield return element;
            }
        }

        /// <summary>
        /// 连接多个序列。
        /// </summary>
        /// <param name="sources">多个序列。</param>
        /// <typeparam name="TSource">序列中成员的类型。</typeparam>
        /// <returns>返回连接在一起的多个序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="sources"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="sources"/> 的成员为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> Concat<TSource>(IEnumerable<IEnumerable<TSource>> sources)
        {
            if (sources == null)
            {
                throw new ArgumentNullException(nameof(sources));
            }

            foreach (var source in sources)
            {
                if (source == null)
                {
                    throw new InvalidOperationException();
                }

                foreach (var element in source)
                {
                    yield return element;
                }
            }
        }

        /// <summary>
        /// 连接多个序列。
        /// </summary>
        /// <param name="sources">多个序列。</param>
        /// <typeparam name="TSource">序列中成员的类型。</typeparam>
        /// <returns>返回连接在一起的多个序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="sources"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="sources"/> 的成员为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> Concat<TSource>(params IEnumerable<TSource>[] sources)
        {
            if (sources == null)
            {
                throw new ArgumentNullException(nameof(sources));
            }

            foreach (var source in sources)
            {
                if (source == null)
                {
                    throw new InvalidOperationException();
                }

                foreach (var element in source)
                {
                    yield return element;
                }
            }
        }

        /// <summary>
        /// 向序列末尾添加默认值，直到序列的成员数达到指定数量。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="totalCount">经过填充以后，序列的总成员数。如果序列本身的成员数等于或大于这个值，则不会进行填充。</param>
        /// <typeparam name="TSource">序列中成员的类型。</typeparam>
        /// <returns>返回填充后的序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="totalCount"/> 为负。</exception>
        public static IEnumerable<TSource> Fill<TSource>(this IEnumerable<TSource> source, int totalCount)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (totalCount < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(totalCount));
            }

            var count = -1;
            foreach (var element in source)
            {
                checked
                {
                    ++count;
                }
                yield return element;
            }
            while (count++ < totalCount)
            {
                yield return default;
            }
        }

        /// <summary>
        /// 向序列末尾添加指定值，直到序列的成员数达到指定数量。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="totalCount">经过填充以后，序列的总成员数。如果序列本身的成员数等于或大于这个值，则不会进行填充。</param>
        /// <param name="fillElement">要用来填充的值。</param>
        /// <typeparam name="TSource">序列中成员的类型。</typeparam>
        /// <returns>返回填充后的序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="totalCount"/> 为负。</exception>
        public static IEnumerable<TSource> Fill<TSource>(this IEnumerable<TSource> source, int totalCount, TSource fillElement)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (totalCount < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(totalCount));
            }

            var count = -1;
            foreach (var element in source)
            {
                checked
                {
                    ++count;
                }
                yield return element;
            }
            while (count++ < totalCount)
            {
                yield return fillElement;
            }
        }

        #endregion

        #region 子序列

        /// <summary>
        /// 将序列按照指定的长度切成多段，然后返回每一段。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">每段的长度。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>返回一个序列，它包含了原序列按指定的长度切开的每一段。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> 小于 1 .</exception>
        public static IEnumerable<IEnumerable<TSource>> ToSegments<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (count < 1)
            {
                throw new ArgumentOutOfRangeException();
            }

            var array    = new TSource[count];
            var position = 0;
            foreach (var element in source)
            {
                array[position] = element;
                if (++position != count)
                {
                    continue;
                }
                position = 0;
                yield return (IEnumerable<TSource>) array.Clone();
            }
            if (position == 0)
            {
                yield break;
            }
            Array.Resize(ref array, position);
            yield return (IEnumerable<TSource>) array.Clone();
        }

        /// <summary>
        /// 将序列按照指定的长度切成多段，然后对每一段的实际长度和每一段自身使用结果选择器函数，构成结果序列。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">每段的长度。</param>
        /// <param name="resultSelector">结果选择器函数。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <typeparam name="TResult">返回值中成员的类型。</typeparam>
        /// <returns>将序列按照指定的长度，然后对每一段的实际长度和每一段自身使用结果选择器函数，构成结果序列，最后返回这个结果序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="resultSelector"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> 小于 1 .</exception>
        public static IEnumerable<TResult> ToSegments<TSource, TResult>(this IEnumerable<TSource> source, int count, Func<int, IEnumerable<TSource>, TResult> resultSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (resultSelector == null)
            {
                throw new ArgumentNullException(nameof(resultSelector));
            }

            if (count < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            var array    = new TSource[count];
            var position = 0;
            foreach (var element in source)
            {
                array[position] = element;
                if (++position != count)
                {
                    continue;
                }
                position = 0;
                yield return resultSelector(count, (IEnumerable<TSource>) array.Clone());
            }
            if (position == 0)
            {
                yield break;
            }
            Array.Resize(ref array, position);
            yield return resultSelector(count, (IEnumerable<TSource>) array.Clone());
        }

        /// <summary>
        /// 生成一个新的序列，它的成员是原序列中每指定的数量个相邻成员组成的序列。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">指定用于构成新序列的成员的原序列中相邻成员的数量。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>返回一个新的序列，它的成员是原序列中每指定的数量个相邻成员组成的序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentException"><paramref name="count"/> 小于 1 .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="source"/> 中的成员数量不足。</exception>
        public static IEnumerable<IEnumerable<TSource>> Each<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (count < 1)
            {
                throw new ArgumentException();
            }

            var array = new TSource[count];
            var index = -1;
            foreach (var element in source)
            {
                checked
                {
                    ++index;
                }
                if (index < count)
                {
                    array[index] = element;
                    if (index == count - 1)
                    {
                        yield return (IEnumerable<TSource>) array.Clone();
                    }
                    continue;
                }
                Array.Copy(array, 1, array, 0, count - 1);
                array[count - 1] = element;
                yield return (IEnumerable<TSource>) array.Clone();
            }
            if (index < count - 1)
            {
                throw new InvalidOperationException($"{nameof(source)} 中的成员数量不足。");
            }
        }

        /// <summary>
        /// 对序列中每指定的数量个相邻成员组成的序列使用结果选择器函数，构成结果序列。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">指定用于结果选择器函数的原序列中相邻成员的数量。</param>
        /// <param name="resultSelector">结果选择器函数。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <typeparam name="TResult">结果的类型。</typeparam>
        /// <returns>对序列中每指定的数量个相邻成员组成的序列使用结果选择器函数，构成结果序列，并返回这个结果序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="resultSelector"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentException"><paramref name="count"/> 小于 1 .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="source"/> 中的成员数量不足。</exception>
        public static IEnumerable<TResult> Each<TSource, TResult>(this IEnumerable<TSource> source, int count, Func<IEnumerable<TSource>, TResult> resultSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (resultSelector == null)
            {
                throw new ArgumentNullException(nameof(resultSelector));
            }

            if (count < 1)
            {
                throw new ArgumentException();
            }

            var array = new TSource[count];
            var index = -1;
            foreach (var element in source)
            {
                checked
                {
                    ++index;
                }
                if (index < count)
                {
                    array[index] = element;
                    if (index == count - 1)
                    {
                        yield return resultSelector((IEnumerable<TSource>) array.Clone());
                    }
                    continue;
                }
                Array.Copy(array, 1, array, 0, count - 1);
                array[count - 1] = element;
                yield return resultSelector((IEnumerable<TSource>) array.Clone());
            }
            if (index < count - 1)
            {
                throw new InvalidOperationException($"{nameof(source)} 中的成员数量不足。");
            }
        }

        #endregion

        #region 其它

        /// <summary>
        /// 返回序列末尾处的指定的数量的成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">数量。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>序列末尾处的指定的数量的成员。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> TakeLast<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var array1  = source.ToArray();
            var length1 = array1.Length;
            var length2 = Math.Max(0, Math.Min(length1, count));
            var array2  = new TSource[length2];
            Array.Copy(array1, length1 - length2, array2, 0, length2);
            return array2;
        }

        /// <summary>
        /// 返回序列的成员，除了末尾处的指定的数量的成员。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">数量。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>除末尾处的指定的数量的成员以外的其它成员。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> SkipLast<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            var array1  = source.ToArray();
            var length1 = array1.Length;
            var length2 = Math.Max(0, Math.Min(length1 - count, length1));
            var array2  = new TSource[length2];
            Array.Copy(array1, 0, array2, 0, length2);
            return array2;
        }

        /// <summary>
        /// 返回序列重复 <paramref name="count"/> 次的结果。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="count">重复次数。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 中的成员的类型。</typeparam>
        /// <returns>序列重复 <paramref name="count"/> 次的结果。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> 为负数。</exception>
        public static IEnumerable<TSource> Repeat<TSource>(this IEnumerable<TSource> source, int count)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            if (count == 0)
            {
                yield break;
            }

            var list = source.ToList();
            while (count-- > 0)
            {
                foreach (var element in list)
                {
                    yield return element;
                }
            }
        }

        /// <summary>
        /// 将多个序列对应位置的成员组成一个中间序列，然后返回由每一个中间序列构成的新序列。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="sources">剩余的序列。</param>
        /// <typeparam name="TSource"><paramref name="source"/> 和 <paramref name="sources"/> 中的成员的类型。</typeparam>
        /// <returns>将多个序列对应位置的成员组成一个中间序列，构成一个新序列，然后返回这个新序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="sources"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="sources"/> 中存在值为 <see langword="null"/> 的成员。</exception>
        public static IEnumerable<IEnumerable<TSource>> Zip<TSource>(this IEnumerable<TSource> source, params IEnumerable<TSource>[] sources)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (sources == null)
            {
                throw new ArgumentNullException(nameof(sources));
            }

            var sourceArray = new IEnumerable<TSource>[sources.Length + 1];
            sourceArray[0] = source;
            Array.Copy(sources, 0, sourceArray, 1, sources.Length);
            var enumerators = Array.ConvertAll(sourceArray, e => e != null ? e.GetEnumerator() : throw new InvalidOperationException($"{sources} 中存在值为 {null} 的成员。"));
            while (enumerators.All(e => e.MoveNext()))
            {
                yield return enumerators.Select(e1 => e1.Current);
            }
            foreach (var element in enumerators)
            {
                element.Dispose();
            }
        }

        /// <summary>
        /// 将多个序列对应位置的成员组成一个中间序列，然后返回由每一个中间序列构成的新序列。
        /// </summary>
        /// <param name="source">序列。</param>
        /// <param name="sources">剩余的序列。</param>
        /// <returns>将多个序列对应位置的成员组成一个中间序列，构成一个新序列，然后返回这个新序列。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> 或 <paramref name="sources"/> 为 <see langword="null"/> .</exception>
        /// <exception cref="InvalidOperationException"><paramref name="sources"/> 中存在值为 <see langword="null"/> 的成员。</exception>
        public static IEnumerable<IEnumerable> Zip(this IEnumerable source, params IEnumerable[] sources)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (sources == null)
            {
                throw new ArgumentNullException(nameof(sources));
            }

            var sourceArray = new IEnumerable[sources.Length + 1];
            sourceArray[0] = source;
            Array.Copy(sources, 0, sourceArray, 1, sources.Length);
            var enumerators = Array.ConvertAll(sourceArray, e => e != null ? e.GetEnumerator() : throw new InvalidOperationException($"{sources} 中存在值为 {null} 的成员。"));
            while (enumerators.All(e => e.MoveNext()))
            {
                yield return enumerators.Select(e1 => e1.Current);
            }
        }

        /// <summary>
        /// 返回两个序列的乘集。
        /// </summary>
        /// <param name="first">第一个序列。</param>
        /// <param name="second">第二个序列。</param>
        /// <param name="resultSelector">将第一个序列中的成员和第二个序列中的成员转换为结果序列的成员的转换函数。</param>
        /// <typeparam name="TFirst"><paramref name="first"/> 中的成员的类型。</typeparam>
        /// <typeparam name="TSecond"><paramref name="second"/> 中的成员的类型。</typeparam>
        /// <typeparam name="TResult">结果序列中的成员的类型。</typeparam>
        /// <returns>返回 <paramref name="first"/> 中的每个成员和 <paramref name="second"/> 中的每个成员经过 <paramref name="resultSelector"/> 转换的结果组成的序列。</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IEnumerable<TResult> Multiply<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            return first.SelectMany(e => second, resultSelector);
        }

        /// <summary>
        /// 通过使用默认的相等比较器对值进行比较，返回两个序列的对称差。
        /// </summary>
        /// <param name="first">第一个序列。</param>
        /// <param name="second">第二个序列。</param>
        /// <typeparam name="TSource"><paramref name="first"/> 和 <paramref name="second"/> 中的成员的类型。</typeparam>
        /// <returns>两个序列的对称差。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> 或 <paramref name="second"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> SymmetricDifference<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            var firstList  = first.ToList();
            var secondList = second.ToList();
            return firstList.Union(secondList).Except(firstList.Intersect(secondList));
        }

        /// <summary>
        /// 通过使用指定的相等比较器对值进行比较，返回两个序列的对称差。
        /// </summary>
        /// <param name="first">第一个序列。</param>
        /// <param name="second">第二个序列。</param>
        /// <param name="comparer">比较器。</param>
        /// <typeparam name="TSource"><paramref name="first"/> 和 <paramref name="second"/> 中的成员的类型。</typeparam>
        /// <returns>两个序列的对称差。</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> 或 <paramref name="second"/> 或 <paramref name="comparer"/> 为 <see langword="null"/> .</exception>
        public static IEnumerable<TSource> SymmetricDifference<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            if (comparer == null)
            {
                throw new ArgumentNullException(nameof(comparer));
            }

            var firstList  = first.ToList();
            var secondList = second.ToList();
            return firstList.Union(secondList, comparer).Except(firstList.Intersect(secondList, comparer));
        }

        /// <summary>
        /// 确定两个序列是否都包含相同的成员。
        /// </summary>
        /// <param name="first">第一个序列。</param>
        /// <param name="second">第二个序列。</param>
        /// <typeparam name="TSource"><paramref name="first"/> 和 <paramref name="second"/> 中的成员的类型。</typeparam>
        /// <returns>两个序列都包含相同的成员吗？</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> 或 <paramref name="second"/> 为 <see langword="null"/> .</exception>
        public static bool SequenceEqualIgnoreOrder<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            return !first.SymmetricDifference(second).Any();
        }

        /// <summary>
        /// 确定两个序列是否都包含相同的成员。
        /// </summary>
        /// <param name="first">第一个序列。</param>
        /// <param name="second">第二个序列。</param>
        /// <param name="comparer">比较器。</param>
        /// <typeparam name="TSource"><paramref name="first"/> 和 <paramref name="second"/> 中的成员的类型。</typeparam>
        /// <returns>两个序列都包含相同的成员吗？</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> 或 <paramref name="second"/> 或 <paramref name="comparer"/> 为 <see langword="null"/> .</exception>
        public static bool SequenceEqualIgnoreOrder<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
        {
            if (first == null)
            {
                throw new ArgumentNullException(nameof(first));
            }

            if (second == null)
            {
                throw new ArgumentNullException(nameof(second));
            }

            if (comparer == null)
            {
                throw new ArgumentNullException(nameof(comparer));
            }

            return !first.SymmetricDifference(second, comparer).Any();
        }

        /// <summary>
        /// 在 0 和 1 之间均匀取样。
        /// </summary>
        /// <param name="count">取样个数。</param>
        /// <returns>取样的结果。</returns>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="count"/> 小于 2 .</exception>
        public static IEnumerable<float> UniformSample(int count)
        {
            if (count < 2)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            yield return 0F;

            if (--count > 1)
            {
                var step = 1D / count;
                for (var i = 1; i < count; i++)
                {
                    yield return (float) (step * i);
                }
            }

            yield return 1F;
        }

        /// <summary>
        /// 在指定的起点和终点之间均匀取样。
        /// </summary>
        /// <param name="from">起点。</param>
        /// <param name="to">终点。</param>
        /// <param name="count">取样个数。</param>
        /// <returns>取样的结果。</returns>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="from"/> 或 <paramref name="to"/> 是 <see cref="float.NaN"/> 、 <see cref="float.PositiveInfinity"/> 或 <see cref="float.NegativeInfinity"/> ， 或 <paramref name="count"/> 小于 2 .</exception>
        public static IEnumerable<float> UniformSample(float from, float to, int count)
        {
            if (float.IsNaN(from) || float.IsInfinity(from))
            {
                throw new ArgumentOutOfRangeException(nameof(from));
            }

            if (float.IsNaN(to) || float.IsInfinity(to))
            {
                throw new ArgumentOutOfRangeException(nameof(to));
            }

            if (count < 2)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            yield return from;

            if (--count > 1)
            {
                var step = ((double) to - from) / count;
                for (var i = 1; i < count; i++)
                {
                    yield return (float) (from + step * i);
                }
            }

            yield return to;
        }

        #endregion
    }
}
